﻿using System;
namespace VXCompany.SilverlightLogging.MVVM.Models.SyncBehaviors
{
	/// <summary>
	/// Base class for SyncBehavior. Helps with threading and synchronisation.
	/// </summary>
	public abstract class SyncBehavior : NotifyingBase, IInternalSyncBehavior
	{
		private static object _lockMe = new object();
		private bool _IsSynchronizing;
		private State _State;
		private Result _LastKnownSyncResult = Result.NoResult;

		/// <summary>
		/// Indicates whether sync is active.
		/// </summary>
		public bool IsSynchronizing
		{
			get { return _IsSynchronizing; }
			private set
			{
				_IsSynchronizing = value;
				RaisePropertyChanged("IsSynchronizing");
			}
		}

		/// <summary>
		/// Gets behavior name.
		/// </summary>
		public virtual string Name
		{
			get { return this.GetType().Name; }
		}

		/// <summary>
		/// Gets current state.
		/// </summary>
		public State State
		{
			get { return _State; }
			set
			{
				_State = value;
				RaisePropertyChanged("State");
			}
		}

		/// <summary>
		/// Gets last known sync result.
		/// </summary>
		public Result LastKnownSyncResult
		{
			get { return _LastKnownSyncResult; }
			set
			{
				_LastKnownSyncResult = value;
				RaisePropertyChanged("LastKnownSyncResult");
			}
		}

		/// <summary>
		/// Sets the operation that handles sync. (usually a service call)
		/// </summary>
		public Action SyncAction
		{
			private get;
			set;
		}

		/// <summary>
		/// Starts behavior
		/// </summary>
		public void Start()
		{
			lock (_lockMe)
			{
				if (State == SyncBehaviors.State.Failed)
				{
					throw new InvalidOperationException("Cannot start behavior in a Failed state.");
				}
				State = SyncBehaviors.State.Started;
			}
			StartImpl();
		}

		/// <summary>
		/// Pauses behavior
		/// </summary>
		public void Pause()
		{
			lock (_lockMe)
			{
				if (State == SyncBehaviors.State.Failed)
				{
					throw new InvalidOperationException("Cannot pause behavior in a Failed state.");
				}
				State = SyncBehaviors.State.Paused;
			}
			PauseImpl();
		}

		/// <summary>
		/// Stops behavior
		/// </summary>
		public void Stop()
		{
			lock (_lockMe)
			{
				IsSynchronizing = false;
				if (State != SyncBehaviors.State.Failed)
				{						
					State = SyncBehaviors.State.Stopped;
				}
			}
			StopImpl();			
		}

		/// <summary>
		/// Syncs exceptionmessages to service
		/// </summary>
		public virtual void BeginSynchronize()
		{
			if (SyncAction == null)
			{
				throw new Exception("Please specify SyncAction");
			}

			//if not already sync'ing and if running
			if (!IsSynchronizing && State == SyncBehaviors.State.Started)
			{
				lock (_lockMe)
				{
					if (!IsSynchronizing && State == SyncBehaviors.State.Started)
					{
						IsSynchronizing = true;
						try
						{
							SyncAction();
						}
						catch (Exception ex)
						{
							ExceptionPolicy.Current.HandleException(ex, ServiceProxy.ErrorLevel.Error);
							EndSynchronize(Result.Failure);
						}
					}
				}
			}
		}

		/// <summary>
		/// Should be called after sync completes.
		/// </summary>
        /// <param name="result">indicates success or failure etc</param>
		public virtual void EndSynchronize(Result result)
		{
			lock (_lockMe)
			{
				if (IsSynchronizing)
				{
					this.LastKnownSyncResult = result;
					IsSynchronizing = false;
				}
			}

			if (!CanContinue(result))
			{
				Stop();
				State = State.Failed;
			}
		}

        /// <summary>
        /// Returns true if sync can continue after the provided result.
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        protected bool CanContinue(Result result)
        {
            return result != Result.Failure 
                && result != Result.Unavailable;
        }

		/// <summary>
		/// Starts behavior
		/// </summary>
		protected abstract void StartImpl();

		/// <summary>
		/// Pauses behavior. Default action is to stop it.
		/// </summary>
		protected virtual void PauseImpl()
		{
			StopImpl();
		}

		/// <summary>
		/// Stops behavior
		/// </summary>
		protected abstract void StopImpl();

	}
}
